package cn.initcap.concurrency.concurrency;

import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.lang.reflect.Method;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Semaphore;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import lombok.extern.slf4j.Slf4j;

/**
 * 线程池运行辅助类
 * 当isParam参数为true 时，代表所要调用的方法需要传入int.class的参数。
 *
 * @author initcap
 * @date Created in 2018/8/1 PM2:03.
 */
@Slf4j
public class ThreadPoolCreatHelper {

    private ThreadPoolCreatHelper() {
        throw new IllegalArgumentException("Utility class !");
    }

    /**
     * 用于创建一个线程池来执行线程安全的测试任务
     *
     * @param clientTotal 将要启动的总访问次数
     * @param threadTotal 所需要的总共线程数量
     * @param clazz       执行的类
     * @param methodName  通过类名反射需要调用的方法名
     */
    public static void threadPoolRun(int clientTotal, int threadTotal, Class<?> clazz, String methodName, String threadName) {
        try {
            threadPoolCreate(clientTotal, threadTotal, clazz, methodName, false, threadName);
        } catch (NoSuchMethodException e) {
            log.info("NoSuchMethodException:{}", e);
        } catch (InterruptedException e) {
            log.info("InterruptedException:{}", e);
            // 当线程阻塞时，调用interrupt方法后，该线程会得到一个interrupt异常，可以通过对该异常的处理而退出线程对于正在运行的线程，没有任何作用！
            Thread.currentThread().interrupt();
        }
    }

    /**
     * 用于创建一个线程池来执行线程安全的测试任务
     *
     * @param clientTotal 将要启动的总访问次数
     * @param threadTotal 所需要的总共线程数量
     * @param clazz       执行的类
     * @param methodName  通过类名反射需要调用的方法名
     */
    public static void threadFinalCountParam(int clientTotal, int threadTotal, Class<?> clazz, String methodName,
                                             String threadName) {
        try {
            threadPoolCreate(clientTotal, threadTotal, clazz, methodName, true, threadName);
        } catch (NoSuchMethodException e) {
            log.info("NoSuchMethodException:{}", e);
        } catch (InterruptedException e) {
            log.info("InterruptedException:{}", e);
            // 当线程阻塞时，调用interrupt方法后，该线程会得到一个interrupt异常，可以通过对该异常的处理而退出线程对于正在运行的线程，没有任何作用！
            Thread.currentThread().interrupt();
        }
    }

    /**
     * 用于创建一个线程池来执行线程安全的测试任务
     *
     * @param clientTotal 将要启动的总访问次数
     * @param threadTotal 所需要的总共线程数量
     * @param clazz       执行的类
     * @param methodName  通过类名反射需要调用的方法名
     * @param isParam     当这个参数为true时代表所需要调用的方法需要传递参数
     * @param threadName  为线程池起名称，方便问题回溯
     * @throws NoSuchMethodException,ClassNotFoundException,InterruptedException 异常
     */
    private static void threadPoolCreate(int clientTotal, int threadTotal, Class<?> clazz, String methodName,
                                         boolean isParam, String threadName) throws NoSuchMethodException, InterruptedException {
        //通过Java的反射机制，通过排序的类名，运行排序函数
        // 通过方法名获取方法
        Method method = isParam ? clazz.getMethod(methodName, int.class) : clazz.getMethod(methodName, null);

        ThreadFactory nameThreadFactory = new ThreadFactoryBuilder().setNameFormat(threadName).build();

        //定义一个线程池
        ExecutorService executorService = new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors(), threadTotal,
                60L, TimeUnit.SECONDS, new SynchronousQueue<>(), nameThreadFactory);
        //定义信号量，指定允许并发的数量
        final Semaphore semaphore = new Semaphore(threadTotal);
        //定义计数器闭锁
        final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
        foreachRun(clientTotal, isParam, method, executorService, semaphore, countDownLatch);
        //保证countDown减为0
        countDownLatch.await();
        //关闭线程池
        executorService.shutdown();
    }

    private static void foreachRun(int clientTotal, boolean isParam, Method method, ExecutorService executorService, Semaphore semaphore, CountDownLatch countDownLatch) {
        for (int i = 0; i < clientTotal; i++) {
            // isParam 为true 时需要初始化为i
            final Integer count = i;
            executorService.execute(() -> {
                try {
                    //当并发量超出 THREAD_TOTAL 时会被阻塞
                    semaphore.acquire();
                    // 调用方法
                    if (isParam) {
                        method.invoke(null, count);
                    } else {
                        method.invoke(null, null);
                    }
                    semaphore.release();
                } catch (Exception e) {
                    log.error("exception", e);
                }
                countDownLatch.countDown();
            });
        }
    }

}
